/*
Copyright (C) 2022-2024 Inspur Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package controller

import (
	"unicode/utf8"

	"github.com/emicklei/go-restful"

	"did-service/auth"
	"did-service/bean/model"
	"did-service/bean/param"
	"did-service/bean/view"
	"did-service/common/constant"
	"did-service/common/http"
	"did-service/common/uuid"
	"did-service/service"
)

// ApplyCredential 可验证凭证申请（用户向发证方）接口
func (dc *DidController) ApplyCredential(req *restful.Request, resp *restful.Response) {
	var para param.ApplyCredentialParam
	if err := req.ReadEntity(&para); err != nil {
		log.Errorf("read entity error: %v", err)
		http.Failed(resp, "读取参数错误!")
		return
	}
	//验证参数
	if ok, errStr := service.ValidatePara(para); !ok {
		http.Failed(resp, errStr)
		return
	}
	//检查发证方状态
	issuerDid, IssuerWalletId, err := dc.srv.ValidateIssuerStatus(para.IssuerId)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "请求验证不通过!")
		return
	}
	//验证发证方id和cptId是否匹配
	err = dc.srv.ValidateIssuerIdAndCptId(para.IssuerId, para.CptId)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "请求验证不通过!")
		return
	}
	//验证CPT属性
	err = dc.srv.ValidateCptClaim(para.CptId, para.CredentialSubject)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "请求验证不通过!")
		return
	}
	//校验credentialSubject
	ownerWalletId := auth.GetCurrentWalletId(req)
	err = dc.srv.ValidateCredSubject(ownerWalletId, para.CredentialSubject)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "请求验证不通过!")
		return
	}
	//校验是否已颁发过凭证
	credId, err := dc.srv.CheckCredIsExistence(para.CptId, issuerDid, para.CredentialSubject.Id)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "请求验证不通过!")
		return
	}
	//存在有效凭证
	if credId != "" {
		//先吊销已存在的凭证
		err = dc.srv.RevokeCredential(IssuerWalletId,
			param.RevokeCredentialParam{CredentialId: credId, RevokeReason: "apply again"})
		if err != nil {
			log.Error(err)
			http.Failed(resp, "吊销已有凭证失败!")
			return
		}
	}

	credId = uuid.Generate()
	//生成可验证凭证
	if err = dc.srv.GenerateCredential(credId, para.IssuerId, issuerDid, para, ownerWalletId); err != nil {
		log.Errorf("err: %v", err)
		http.Failed(resp, "凭证生成失败!")
		return
	}
	log.Infof("generate credential, credId: %s", credId)

	credResp := model.CredentialResponse{RequestId: credId}
	http.OkWithData(resp, "成功", credResp)
}

// RevokeCredential 凭证吊销接口
func (dc *DidController) RevokeCredential(req *restful.Request, resp *restful.Response) {
	currentWalletId := auth.GetCurrentWalletId(req)

	var para param.RevokeCredentialParam
	if err := req.ReadEntity(&para); err != nil {
		log.Errorf("read entity error: %v", err)
		http.Failed(resp, "读取参数错误!")
		return
	}
	if utf8.RuneCountInString(para.RevokeReason) > 250 {
		http.Failed(resp, "revokeReason:撤销原因应小于250个字符！")
		return
	}
	//验证参数
	if ok, errStr := service.ValidatePara(para); !ok {
		http.Failed(resp, errStr)
		return
	}

	exist, valid, err := dc.srv.CheckCredIsExistenceById(para.CredentialId)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "凭证吊销失败!")
		return
	}
	if !exist {
		http.Failed(resp, "凭证不存在!")
		return
	}
	if !valid {
		log.Infof("the credential has been revoked, Id: %s", para.CredentialId)
		http.Failed(resp, "凭证已被吊销!")
		return
	}

	//吊销可验证凭证
	if err := dc.srv.RevokeCredential(currentWalletId, para); err != nil {
		log.Error(err)
		http.Failed(resp, "凭证吊销失败!")
		return
	}
	log.Infof("revoke credential, credId: %s", para.CredentialId)
	http.Ok(resp, "成功")
}

// RevocationQuery 凭证吊销查询接口
func (dc *DidController) RevocationQuery(req *restful.Request, resp *restful.Response) {
	credId := req.QueryParameter("credentialId")
	if credId == "" {
		log.Errorf("credential id can not be empty!")
		http.Failed(resp, "凭证ID不能为空!")
		return
	}
	exist, _, err := dc.srv.CheckCredIsExistenceById(credId)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "查询失败!")
		return
	}
	if !exist {
		http.Failed(resp, "凭证不存在!")
		return
	}

	//确认凭证是否已被吊销
	revoked, detail, err := dc.srv.CheckCredIsRevoked(credId)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "查询失败!")
		return
	}
	revocationDetail := view.RevocationResp{
		Revoked: revoked,
	}
	if revoked {
		revocationDetail.RevokeDate = detail.RevokeDate.Format(constant.TimeLayout)
		revocationDetail.RevokeReason = detail.RevokeReason
		revocationDetail.RevokeSignature = detail.RevokeSignature
	}
	http.OkWithData(resp, "成功", revocationDetail)
}

// AcquireCredential 凭证获取接口
func (dc *DidController) AcquireCredential(req *restful.Request, resp *restful.Response) {
	credId := req.QueryParameter("credentialId")
	if credId == "" {
		log.Errorf("credential id can not be empty!")
		http.Failed(resp, "凭证ID不能为空!")
		return
	}
	walletId := auth.GetCurrentWalletId(req)
	cred, err := dc.srv.AcquireCredential(credId, walletId)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "获取凭证失败!")
		return
	}
	http.OkWithData(resp, "成功", cred)
}

// CredentialList 用户凭证列表
func (dc *DidController) CredentialList(req *restful.Request, resp *restful.Response) {
	para := param.ListCredentialParam{}
	para.PageNum = req.QueryParameter("pageNum")
	para.PageSize = req.QueryParameter("pageSize")
	para.IssuerName = req.QueryParameter("issuerName")
	para.CredentialName = req.QueryParameter("credentialName")

	walletId := auth.GetCurrentWalletId(req)
	credList, err := dc.srv.QueryCredentialList(walletId, &para, true)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "获取凭证列表失败!")
		return
	}
	http.OkWithData(resp, "成功", credList)
}

// SimpleCredentialList 用户凭证列表(只包含凭证ID、名称)
func (dc *DidController) SimpleCredentialList(req *restful.Request, resp *restful.Response) {
	currentWalletId := auth.GetCurrentWalletId(req)
	userType := auth.GetCurrentUserType(req)
	if userType != constant.UserTypePerson && userType != constant.UserTypeEnterprise {
		http.Failed(resp, "此用户无法获取凭证列表!")
		return
	}
	credList, err := dc.srv.QuerySimpleCredentialList(currentWalletId)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "获取凭证列表失败!")
		return
	}
	http.OkWithData(resp, "成功", credList)
}

// RevocationList 凭证吊销列表
func (dc *DidController) RevocationList(req *restful.Request, resp *restful.Response) {
	para := param.ListCredentialParam{}
	para.PageNum = req.QueryParameter("pageNum")
	para.PageSize = req.QueryParameter("pageSize")
	para.IssuerName = req.QueryParameter("issuerName")
	para.CredentialName = req.QueryParameter("credentialName")

	walletId := auth.GetCurrentWalletId(req)
	RevocList, err := dc.srv.QueryCredentialList(walletId, &para, false)
	if err != nil {
		log.Errorf("error: %v", err)
		http.Failed(resp, "获取吊销列表失败!")
		return
	}
	http.OkWithData(resp, "成功", RevocList)
}

func (dc *DidController) CredentialDetail(req *restful.Request, resp *restful.Response) {
	//获取当前钱包ID
	walletId := auth.GetCurrentWalletId(req)

	credentialId := req.PathParameter("credentialId")
	if credentialId == "" {
		log.Errorf("credential id can not be empty!")
		http.Failed(resp, "凭证编号不能为空!")
		return
	}

	data, err := dc.srv.QueryCredentialDetail(walletId, credentialId)
	if err != nil {
		log.Errorf("err: %s", err)
		http.Failed(resp, "查询失败！")
		return
	}
	http.OkWithData(resp, "成功", data)
}

func (dc *DidController) CredentialClaims(req *restful.Request, resp *restful.Response) {
	//获取当前钱包ID
	walletId := auth.GetCurrentWalletId(req)
	userType := auth.GetCurrentUserType(req)
	if userType != constant.UserTypePerson && userType != constant.UserTypeEnterprise {
		http.Failed(resp, "此用户无法获取凭证claims!")
		return
	}

	credentialId := req.PathParameter("credentialId")
	if credentialId == "" {
		log.Errorf("credential id can not be empty!")
		http.Failed(resp, "凭证编号不能为空!")
		return
	}

	data, err := dc.srv.QueryCredentialClaims(walletId, credentialId)
	if err != nil {
		log.Errorf("err: %s", err)
		http.Failed(resp, "查询失败！")
		return
	}
	http.OkWithData(resp, "成功", data)
}

func (ctl *DidController) GetCredentialByDid(req *restful.Request, resp *restful.Response) {
	did := req.PathParameter("did")
	cptId := req.PathParameter("cptId")
	if cptId == "" {
		log.Errorf("cpt id can not be empty!")
		http.Failed(resp, "cptId不能为空!")
		return
	}

	if did == "" {
		log.Errorf("did can not be empty!")
		http.Failed(resp, "did不能为空!")
		return
	}
	//获取当前钱包ID
	walletId := auth.GetCurrentWalletId(req)

	vcEntity, err := ctl.srv.GetCredentialByDid(cptId, did)
	if err != nil {
		log.Errorf("get vc by did error: %v", err)
		http.Failed(resp, "获取失败")
		return
	}

	if vcEntity == nil {
		http.Failed(resp, "获取失败，凭证不存在")
		return
	}

	data, err := ctl.srv.QueryCredentialDetail(walletId, vcEntity.Id)
	if err != nil {
		log.Errorf("err: %s", err)
		http.Failed(resp, "查询失败！")
		return
	}
	http.OkWithData(resp, "成功", data)

}
